Skip to content

feat(metrics): Add drag-and-drop reordering to metric panels#112671

Merged
nsdeschenes merged 28 commits intomasterfrom
nd/EXP-827/feat-tracemetrics-add-dnd-to-metrics-page
Apr 14, 2026
Merged

feat(metrics): Add drag-and-drop reordering to metric panels#112671
nsdeschenes merged 28 commits intomasterfrom
nd/EXP-827/feat-tracemetrics-add-dnd-to-metrics-page

Conversation

@nsdeschenes
Copy link
Copy Markdown
Contributor

@nsdeschenes nsdeschenes commented Apr 10, 2026

Users with multiple metric panels can now grab the drag handle in the toolbar and reorder panels. The new order is persisted to URL query params so it survives page reloads. During a drag, chart content is replaced with a lightweight placeholder to avoid expensive re-renders of ECharts instances.

Key details:

  • Drag handle is hidden when there's only one panel
  • Stable sortable keys handle duplicate queries and mid-list insertions
  • Keyboard and screen reader accessibility via dnd-kit sensors and forwarded ARIA attributes
  • useReorderMetricQueries hook encodes the reordered list back into URL params

Closes EXP-827

Example:

Screen.Recording.2026-04-10.at.11.58.27.mov

nsdeschenes and others added 8 commits April 10, 2026 11:11
Add a hook that encodes a reordered list of metric queries into the
URL query params and navigates, providing the foundation for
drag-and-drop reordering of metric panels.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…olbar

MetricPanel now accepts ref, style, dragListeners, and isAnyDragging
props. When a drag is active, chart content is replaced with a
placeholder to avoid expensive re-renders. MetricToolbar renders a
DragReorderButton when drag listeners are provided.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add SortableMetricPanel wrapper using @dnd-kit/sortable and integrate
DndContext into MetricsTabBodySection. Panels can now be reordered via
drag-and-drop, with the new order persisted to URL query params.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Hide the drag reorder button and remove its grid column when there is
only a single metric query, since reordering is not meaningful with
one item.

Co-Authored-By: Claude Opus 4.6 <noreply@example.com>
Forward dnd-kit aria attributes through MetricPanel to the DOM so
keyboard and screen reader users can interact with sortable panels.

Replace index-based uniqueId array with a Map keyed by encoded query
params so deletions and mid-list insertions no longer desync React
keys with their queries.

Move offsetHeight measurement from the render body into a
useLayoutEffect to avoid forced synchronous reflow.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add dependency array to useLayoutEffect to avoid forcing layout reflow
on every render. Include index in sortable key generation so duplicate
metric queries get distinct stable IDs. Wrap sortableItems in useMemo
so onDragEnd useCallback actually memoizes. Fix leading space in grid
columns template when drag handle is absent.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previously all panels displayed the placeholder text during drag.
Now only the panel being actively dragged shows the message, while
other panels show a blank placeholder to avoid chart rendering.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…component

Replace useLayoutEffect height capture with a ref callback on the
container, removing an extra ref and effect. Extract the drag
placeholder into a DnDPlaceholder component for clarity.

Co-Authored-By: Claude Opus 4.6 <noreply@example.com>
@linear-code
Copy link
Copy Markdown

linear-code bot commented Apr 10, 2026

@github-actions github-actions bot added the Scope: Frontend Automatically applied to PRs that change frontend components label Apr 10, 2026
nsdeschenes and others added 2 commits April 10, 2026 11:54
…lines

Front-load the informational content before the personality quip,
per content & voice guidelines.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The sortable key included the array index, so every reorder changed
moved items' keys, causing React to unmount and remount panels. This
lost local state and forced full ECharts re-initialization — the
exact cost the placeholder optimization was designed to avoid.

Use occurrence count instead of index to disambiguate duplicate
queries, keeping keys stable across reorders.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
nsdeschenes and others added 2 commits April 10, 2026 12:02
Move the sortable dnd hook out of metricsTab.tsx into a dedicated
hooks file for better organization.

Co-Authored-By: Claude Opus 4.6 <noreply@example.com>
@nsdeschenes nsdeschenes marked this pull request as ready for review April 10, 2026 15:14
@nsdeschenes nsdeschenes requested a review from a team as a code owner April 10, 2026 15:14
nsdeschenes and others added 5 commits April 10, 2026 13:50
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Show the Samples/Aggregates tab list with placeholder content on the
table side during drag-and-drop reordering, giving a better visual
indication of the panel layout.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wrap SideBySideOrientation in a React Activity so it stays mounted
but hidden during drag-and-drop reordering. This prevents the table
from refetching data when dragging ends.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace Map iterator .forEach() with for...of loop for broader JS
engine compatibility. Simplify the DnD table placeholder to use a
plain Placeholder instead of rendering MetricInfoTabList outside its
required TabStateProvider context.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit d5eb0ae. Configure here.

nsdeschenes and others added 2 commits April 10, 2026 14:59
MetricInfoTabList is only used internally within its module, so the
export keyword is unnecessary.

Co-Authored-By: Claude Opus 4.6 <noreply@example.com>
The tab list (Samples/Aggregates) should render before additionalActions
in the Flex container so it appears on the left, with the
PanelPositionSelector and HideContentButton on the right.

Co-Authored-By: Claude Opus 4.6 <noreply@example.com>
nsdeschenes and others added 2 commits April 13, 2026 15:11
…cemetrics-add-dnd-to-metrics-page

# Conflicts:
#	static/app/views/explore/metrics/metricPanel/index.tsx
#	static/app/views/explore/metrics/metricToolbar/index.tsx
#	static/app/views/explore/metrics/metricsTab.tsx
Move stable label state alongside metric query reorder operations so drag-and-drop does not relabel toolbar badges by position.

Add a regression test that verifies labels remain attached to query identity after reorder.

Co-Authored-By: Codex <noreply@openai.com>
expect(result.current[1]).toEqual(expect.objectContaining({label: 'ƒ1'}));
});

it('keeps labels attached to query identity when reordering', () => {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🙏


const sensors = useSensors(
useSensor(PointerSensor),
useSensor(KeyboardSensor, {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoa, keyboard navigation for DnD is so cool.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah!! I was impressed trying it out, they really nailed it. What a relief for accessibility too.

@narsaynorath
Copy link
Copy Markdown
Member

Ah, one thing I noticed was, we tried to make inserts for metric queries add between aggregates and equations and it gets a little funky now that we can also rearrange functions. Is it possible to do something like.. If you haven't re-arranged them then they add between aggregates and equations, otherwise we can just append them to the bottom?

Not entirely sure what the best way to handle it is, but this seems reasonable. Open to suggestions on dealing with these! This doesn't need to block your PR though if you want to handle it separately

Replace the metrics drag-and-drop UUID bookkeeping with the stable
query labels that now persist across query mutations.

Keep the sortable panel IDs and React keys aligned with those labels,
and add a focused hook test that covers duplicate-query reordering.

Co-Authored-By: Codex <noreply@openai.com>
@nsdeschenes
Copy link
Copy Markdown
Contributor Author

Ah, one thing I noticed was, we tried to make inserts for metric queries add between aggregates and equations and it gets a little funky now that we can also rearrange functions. Is it possible to do something like.. If you haven't re-arranged them then they add between aggregates and equations, otherwise we can just append them to the bottom?

Not entirely sure what the best way to handle it is, but this seems reasonable. Open to suggestions on dealing with these! This doesn't need to block your PR though if you want to handle it separately

I don't think this should be too hard, we just need to split up them into two DnD contexts', i'll take a stab at it here

nsdeschenes and others added 2 commits April 14, 2026 10:54
Allow the sortable metrics hook to expose a filtered view while still
reordering against the original query list. This keeps sectioned views
stable and ignores drops that cross section boundaries.

Co-Authored-By: Codex <noreply@openai.com>
Render aggregate queries and equations in their own sortable sections on
the refreshed metrics tab. This keeps each panel group visually distinct
while preserving the drag state needed for section-local reordering.

Co-Authored-By: Codex <noreply@openai.com>
@sentry
Copy link
Copy Markdown
Contributor

sentry bot commented Apr 14, 2026

Sentry Snapshot Testing

Name Added Removed Modified Renamed Unchanged Status
sentry-frontend
sentry-frontend
0 0 0 0 204 ✅ Unchanged

Copy link
Copy Markdown
Member

@narsaynorath narsaynorath left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀

@nsdeschenes nsdeschenes merged commit 66f191f into master Apr 14, 2026
65 checks passed
@nsdeschenes nsdeschenes deleted the nd/EXP-827/feat-tracemetrics-add-dnd-to-metrics-page branch April 14, 2026 15:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Scope: Frontend Automatically applied to PRs that change frontend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants